# 认证 RESTful API 控制器 v2
from flask import request, current_app
from flask_restx import Resource, abort
from flask_jwt_extended import (
    jwt_required, get_jwt_identity, create_access_token, create_refresh_token,
    jwt_refresh_token_required, get_raw_jwt
)
import uuid
from datetime import datetime

from app.extensions import db
from app.models.user import User
from app.utils.response import success_response, error_response
from .. import auth_ns
from ..dto.auth_dto import (
    login_model, register_model, token_model, refresh_model,
    password_reset_request_model, password_reset_confirm_model,
    password_change_model, two_factor_setup_model, two_factor_verify_model
)
from ..dto.common_dto import create_response_models

# 创建响应模型
success_response_model, error_response_model = create_response_models(auth_ns)

# 存储撤销的令牌（在生产环境中应使用Redis等）
revoked_tokens = set()

@auth_ns.route('/login')
class LoginResource(Resource):
    @auth_ns.doc('user_login_v2')
    @auth_ns.expect(login_model, validate=True)
    @auth_ns.marshal_with(token_model)
    @auth_ns.response(401, '认证失败', error_response_model)
    def post(self):
        """用户登录 - v2版本"""
        data = request.get_json()
        username_or_email = data['username']
        password = data['password']
        remember_me = data.get('remember_me', False)  # v2 新增
        
        # 查找用户（用户名或邮箱）
        user = User.query.filter(
            (User.username == username_or_email) | (User.email == username_or_email)
        ).first()
        
        # 验证用户和密码
        if not user or not user.check_password(password) or not user.is_active:
            abort(401, message='用户名/邮箱或密码错误')
        
        # v2 增强：更新登录信息
        user.last_login = datetime.utcnow()
        user.login_count = (user.login_count or 0) + 1
        db.session.commit()
        
        # 创建访问令牌和刷新令牌
        # v2 增强：根据记住我选项设置令牌过期时间
        if remember_me:
            expires_delta = current_app.config['JWT_ACCESS_TOKEN_EXPIRES'] * 7  # 记住7天
        else:
            expires_delta = current_app.config['JWT_ACCESS_TOKEN_EXPIRES']
            
        access_token = create_access_token(identity=user.id, expires_delta=expires_delta)
        refresh_token = create_refresh_token(identity=user.id)
        
        # v2 增强：返回用户基本信息和权限
        user_info = {
            'id': user.id,
            'username': user.username,
            'email': user.email,
            'nickname': user.nickname,
            'is_admin': user.is_admin,
            'roles': [role.name for role in user.roles]
        }
        
        # 获取用户权限
        permissions = []
        for role in user.roles:
            for permission in role.permissions:
                permissions.append(permission.name)
        
        return {
            'access_token': access_token,
            'refresh_token': refresh_token,
            'token_type': 'Bearer',
            'expires_in': expires_delta.total_seconds(),
            'user_info': user_info,
            'permissions': permissions
        }

@auth_ns.route('/register')
class RegisterResource(Resource):
    @auth_ns.doc('user_register_v2')
    @auth_ns.expect(register_model, validate=True)
    @auth_ns.marshal_with(token_model)
    @auth_ns.response(400, '无效请求', error_response_model)
    def post(self):
        """用户注册 - v2版本"""
        data = request.get_json()
        
        # v2 增强：检查服务条款同意
        if not data.get('agree_terms', False):
            abort(400, message='必须同意服务条款才能注册')
        
        # 验证密码确认
        if data['password'] != data['confirm_password']:
            abort(400, message='密码和确认密码不匹配')
        
        # 检查用户名是否已存在
        if User.query.filter_by(username=data['username']).first():
            abort(400, message='用户名已存在')
        
        # 检查邮箱是否已存在
        if User.query.filter_by(email=data['email']).first():
            abort(400, message='邮箱已存在')
        
        # 创建新用户
        user = User(
            username=data['username'],
            email=data['email'],
            nickname=data.get('nickname', data['username'])
        )
        
        # 设置密码
        user.set_password(data['password'])
        
        # v2 增强：添加默认值
        user.verified = False  # 新用户需要验证邮箱
        user.login_count = 0
        user.profile_views = 0
        
        # 保存到数据库
        db.session.add(user)
        db.session.commit()
        
        # 创建访问令牌和刷新令牌
        access_token = create_access_token(identity=user.id)
        refresh_token = create_refresh_token(identity=user.id)
        
        # v2 增强：返回用户基本信息和权限
        user_info = {
            'id': user.id,
            'username': user.username,
            'email': user.email,
            'nickname': user.nickname,
            'is_admin': user.is_admin,
            'roles': [role.name for role in user.roles]
        }
        
        return {
            'access_token': access_token,
            'refresh_token': refresh_token,
            'token_type': 'Bearer',
            'expires_in': current_app.config['JWT_ACCESS_TOKEN_EXPIRES'].total_seconds(),
            'user_info': user_info,
            'permissions': []
        }, 201

@auth_ns.route('/refresh')
class RefreshResource(Resource):
    @auth_ns.doc('refresh_token_v2')
    @auth_ns.expect(refresh_model, validate=True)
    @auth_ns.marshal_with(token_model)
    @auth_ns.response(401, '无效令牌', error_response_model)
    def post(self):
        """刷新访问令牌 - v2版本"""
        data = request.get_json()
        refresh_token = data['refresh_token']
        
        try:
            # 验证刷新令牌并获取用户ID
            from flask_jwt_extended import decode_token
            decoded_token = decode_token(refresh_token)
            user_id = decoded_token['identity']
            
            # 检查用户是否存在且激活
            user = User.query.get(user_id)
            if not user or not user.is_active:
                abort(401, message='无效用户')
            
            # 创建新的访问令牌
            access_token = create_access_token(identity=user_id)
            
            # v2 增强：返回用户基本信息和权限
            user_info = {
                'id': user.id,
                'username': user.username,
                'email': user.email,
                'nickname': user.nickname,
                'is_admin': user.is_admin,
                'roles': [role.name for role in user.roles]
            }
            
            # 获取用户权限
            permissions = []
            for role in user.roles:
                for permission in role.permissions:
                    permissions.append(permission.name)
            
            return {
                'access_token': access_token,
                'refresh_token': refresh_token,  # 刷新令牌可以重复使用
                'token_type': 'Bearer',
                'expires_in': current_app.config['JWT_ACCESS_TOKEN_EXPIRES'].total_seconds(),
                'user_info': user_info,
                'permissions': permissions
            }
        except Exception:
            abort(401, message='无效的刷新令牌')

@auth_ns.route('/logout')
class LogoutResource(Resource):
    @auth_ns.doc('user_logout_v2')
    @auth_ns.marshal_with(success_response_model)
    @auth_ns.response(401, '未授权', error_response_model)
    @jwt_required()
    def post(self):
        """用户登出 - v2版本"""
        jti = get_raw_jwt()['jti']
        revoked_tokens.add(jti)
        
        # v2 增强：添加请求ID和时间戳
        request_id = str(uuid.uuid4())
        
        return {
            'success': True, 
            'message': '成功登出',
            'timestamp': datetime.utcnow().isoformat(),
            'request_id': request_id
        }

@auth_ns.route('/password-reset')
class PasswordResetResource(Resource):
    @auth_ns.doc('request_password_reset_v2')
    @auth_ns.expect(password_reset_request_model, validate=True)
    @auth_ns.marshal_with(success_response_model)
    @auth_ns.response(404, '用户未找到', error_response_model)
    def post(self):
        """请求密码重置 - v2版本"""
        data = request.get_json()
        email = data['email']
        
        user = User.query.filter_by(email=email).first()
        if not user:
            # 为了安全，即使用户不存在也返回成功消息
            return {
                'success': True, 
                'message': '如果该邮箱存在，重置链接已发送',
                'timestamp': datetime.utcnow().isoformat(),
                'request_id': str(uuid.uuid4())
            }
        
        # 在实际应用中，这里应该生成重置令牌并发送邮件
        # 为了示例，我们只返回成功消息
        return {
            'success': True, 
            'message': '如果该邮箱存在，重置链接已发送',
            'timestamp': datetime.utcnow().isoformat(),
            'request_id': str(uuid.uuid4())
        }

@auth_ns.route('/password-reset/confirm')
class PasswordResetConfirmResource(Resource):
    @auth_ns.doc('confirm_password_reset_v2')
    @auth_ns.expect(password_reset_confirm_model, validate=True)
    @auth_ns.marshal_with(success_response_model)
    @auth_ns.response(400, '无效请求', error_response_model)
    def post(self):
        """确认密码重置 - v2版本"""
        data = request.get_json()
        
        # 验证密码确认
        if data['new_password'] != data['confirm_password']:
            abort(400, message='密码和确认密码不匹配')
        
        # 在实际应用中，这里应该验证重置令牌
        # 为了示例，我们只返回成功消息
        return {
            'success': True, 
            'message': '密码重置成功',
            'timestamp': datetime.utcnow().isoformat(),
            'request_id': str(uuid.uuid4())
        }

@auth_ns.route('/password-change')
class PasswordChangeResource(Resource):
    @auth_ns.doc('change_password_v2')
    @auth_ns.expect(password_change_model, validate=True)
    @auth_ns.marshal_with(success_response_model)
    @auth_ns.response(401, '认证失败', error_response_model)
    @jwt_required()
    def post(self):
        """修改密码 - v2版本"""
        data = request.get_json()
        current_user_id = get_jwt_identity()
        user = User.query.get(current_user_id)
        
        # 验证旧密码
        if not user.check_password(data['old_password']):
            abort(401, message='旧密码不正确')
        
        # 验证新密码确认
        if data['new_password'] != data['confirm_password']:
            abort(400, message='新密码和确认密码不匹配')
        
        # 设置新密码
        user.set_password(data['new_password'])
        db.session.commit()
        
        return {
            'success': True, 
            'message': '密码修改成功',
            'timestamp': datetime.utcnow().isoformat(),
            'request_id': str(uuid.uuid4())
        }

# v2 新增：双因素认证设置
@auth_ns.route('/2fa/setup')
class TwoFactorSetupResource(Resource):
    @auth_ns.doc('setup_2fa_v2')
    @auth_ns.marshal_with(two_factor_setup_model)
    @auth_ns.response(401, '未授权', error_response_model)
    @jwt_required()
    def post(self):
        """设置双因素认证 - v2新增功能"""
        current_user_id = get_jwt_identity()
        user = User.query.get(current_user_id)
        
        # 在实际应用中，这里应该生成二步验证密钥和备用恢复码
        # 为了示例，我们返回模拟数据
        secret = "JBSWY3DPEHPK3PXP"  # 示例密钥
        qr_code = f"otpauth://totp/FlaskApp:{user.username}?secret={secret}&issuer=FlaskApp"
        backup_codes = ["123456", "234567", "345678", "456789", "567890"]
        
        return {
            'secret': secret,
            'qr_code': qr_code,
            'backup_codes': backup_codes
        }

# v2 新增：双因素认证验证
@auth_ns.route('/2fa/verify')
class TwoFactorVerifyResource(Resource):
    @auth_ns.doc('verify_2fa_v2')
    @auth_ns.expect(two_factor_verify_model, validate=True)
    @auth_ns.marshal_with(success_response_model)
    @auth_ns.response(401, '验证失败', error_response_model)
    @jwt_required()
    def post(self):
        """验证双因素认证 - v2新增功能"""
        data = request.get_json()
        code = data['code']
        
        # 在实际应用中，这里应该验证二步验证码
        # 为了示例，我们假设任何6位数字都是有效的
        if len(code) == 6 and code.isdigit():
            return {
                'success': True, 
                'message': '双因素认证验证成功',
                'timestamp': datetime.utcnow().isoformat(),
                'request_id': str(uuid.uuid4())
            }
        else:
            abort(401, message='无效的验证码')